// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: daniel

#pragma once

#include <memory>
#include <set>
#include <string>
#include <utility>

#include "coin/proto/coin_order_enums.pb.h"
#include "coin2/exchange/base/api_base/asio_http_context.h"
#include "coin2/exchange/base/order/parser.h"

namespace coin2::exchange::gateio::order_v4 {

class GateioParser : public base::order::BaseParser {
  using JsonValue = base::order::BaseParser::JsonValue;
  using SubmitOrderResponse = base::order::BaseParser::SubmitOrderResponse;
  using ProductOrderBundle = base::order::BaseParser::ProductOrderBundle;
  using ProductFillBundle = base::order::BaseParser::ProductFillBundle;
  using AccountBalance = base::order::BaseParser::AccountBalance;
  using ProductOrderElement = base::order::BaseParser::ProductOrderElement;
  using ProductFillElement = base::order::BaseParser::ProductFillElement;
  using AccountPosition = base::order::BaseParser::AccountPosition;
  using OrderErrorCode = base::order::BaseParser::OrderErrorCode;
  using OrderSubTopicId = base::order::BaseParser::OrderSubTopicId;
  using OrderSide = coin::proto::OrderSide;
  using OrderType = coin::proto::OrderType;
  using CurrencyPairs = std::set<std::pair<std::string, std::string>>;

 public:
  const ParseResult& ParseRawMessage(const RawMessage& msg) override;
  AccountBalance ParseBalance(const JsonValue& doc) override;
  bool ParseRawBalance(const JsonValue& doc, RawBalanceMap* raw_balance_map);

 private:
  void ParseSubmitResponse();
  void ParseBatchSubmitResponse();
  void ParseCancelResponse();
  void ParseBatchCancelResponse();
  void ParseRestAccountBalance();
  void ParseRestOpenOrdersInfo();
  void ParseRestOpenOrdersInfoV2();
  void ParseRestHistOrdersInfo();
  void ParseRestFillInfoList();
  void ParseRestDeposit();
  void ParseRestWithdraw();
  void ParseRestTransfers();

  void ParseWsOrderInfoList(const JsonDocument& document);
  void ParseWsTradeInfoList(const JsonDocument& document);
  void ParseWsBalanceInfoList(const JsonDocument& document);
  void ParseWsMessage();

  void ParseRestOrder(const JsonValue& data, ProductOrderElement* product_order_element);
  void ParseRestFillInfo(const JsonValue& data, ProductFillElement* product_fill_element);

  inline bool RawInfoParseJsonAndCheck(JsonDocument& document, std::string fun_name);

  int64_t GetTimestamp(const std::string& time_str) const;
  bool IsGateioErrMessage(const JsonValue& data) const;
  std::optional<std::string> GetExchangeErrorCode(const JsonValue& data) const;
  std::optional<std::string> GetExchangeErrorMsg(const JsonValue& data) const;
  OrderErrorCode GetErrorCode(const std::string& error_code) const;
  int64_t GetProcOrderId(const JsonValue& data) const;
  OrderSide GetOrderSide(const std::string& side) const;
  OrderSide GetOrderSide(int64_t type) const;
  OrderType GetOrderType(const std::string& type) const;
  OrderType GetOrderType(int64_t orderType) const;
  void GetOrderStatus(const JsonValue& data, ProductOrderElement* product_order_element);
  void SetOrderStatus(const JsonValue& data, ProductOrderElement* product_order_element);
  const CurrencyPairs& native_currencies() const;

  std::map<std::string, std::string> transfer_deposit_map_;
  bool is_transfer_deposit_initialized_ = false;
  std::map<std::string, std::string> transfer_withdrawal_map_;
  bool is_transfer_withdrawal_initialized_ = false;
  std::map<std::string, std::string> transfer_subaccount_map_;
  bool is_transfer_subaccount_initialized_ = false;
};

}  // namespace coin2::exchange::gateio::order_v4
